From 6de4c63a67ba8b200ee9c79d985d9fa2c252a279 Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Wed, 4 Apr 2018 13:22:28 +0200 Subject: [PATCH] inspector: Bring back debug updates This time, they are implemented as an overlay, so they require a running inspector and can't be enabled via env variable anymore. --- gtk/inspector/highlightoverlay.c | 1 + gtk/inspector/inspectoroverlay.c | 4 +- gtk/inspector/inspectoroverlay.h | 2 + gtk/inspector/meson.build | 1 + gtk/inspector/updatesoverlay.c | 281 +++++++++++++++++++++++++++++++ gtk/inspector/updatesoverlay.h | 34 ++++ gtk/inspector/visual.c | 43 +++++ gtk/inspector/visual.ui | 28 +++ gtk/inspector/window.c | 2 +- 9 files changed, 394 insertions(+), 2 deletions(-) create mode 100644 gtk/inspector/updatesoverlay.c create mode 100644 gtk/inspector/updatesoverlay.h diff --git a/gtk/inspector/highlightoverlay.c b/gtk/inspector/highlightoverlay.c index 869262e148..c45220bc0e 100644 --- a/gtk/inspector/highlightoverlay.c +++ b/gtk/inspector/highlightoverlay.c @@ -42,6 +42,7 @@ G_DEFINE_TYPE (GtkHighlightOverlay, gtk_highlight_overlay, GTK_TYPE_INSPECTOR_OV static void gtk_highlight_overlay_snapshot (GtkInspectorOverlay *overlay, GtkSnapshot *snapshot, + GskRenderNode *node, GtkWidget *widget) { GtkHighlightOverlay *self = GTK_HIGHLIGHT_OVERLAY (overlay); diff --git a/gtk/inspector/inspectoroverlay.c b/gtk/inspector/inspectoroverlay.c index a2e1c3d070..9b9678d0ae 100644 --- a/gtk/inspector/inspectoroverlay.c +++ b/gtk/inspector/inspectoroverlay.c @@ -34,6 +34,7 @@ G_DEFINE_ABSTRACT_TYPE (GtkInspectorOverlay, gtk_inspector_overlay, G_TYPE_OBJEC static void gtk_inspector_overlay_default_snapshot (GtkInspectorOverlay *self, GtkSnapshot *snapshot, + GskRenderNode *node, GtkWidget *widget) { } @@ -58,9 +59,10 @@ gtk_inspector_overlay_init (GtkInspectorOverlay *self) void gtk_inspector_overlay_snapshot (GtkInspectorOverlay *self, GtkSnapshot *snapshot, + GskRenderNode *node, GtkWidget *widget) { - GTK_INSPECTOR_OVERLAY_GET_CLASS (self)->snapshot (self, snapshot, widget); + GTK_INSPECTOR_OVERLAY_GET_CLASS (self)->snapshot (self, snapshot, node, widget); } void diff --git a/gtk/inspector/inspectoroverlay.h b/gtk/inspector/inspectoroverlay.h index 867070b3c5..a38e77726e 100644 --- a/gtk/inspector/inspectoroverlay.h +++ b/gtk/inspector/inspectoroverlay.h @@ -34,12 +34,14 @@ struct _GtkInspectorOverlayClass void (* snapshot) (GtkInspectorOverlay *self, GtkSnapshot *snapshot, + GskRenderNode *node, GtkWidget *widget); void (* queue_draw) (GtkInspectorOverlay *self); }; void gtk_inspector_overlay_snapshot (GtkInspectorOverlay *self, GtkSnapshot *snapshot, + GskRenderNode *node, GtkWidget *widget); void gtk_inspector_overlay_queue_draw (GtkInspectorOverlay *self); diff --git a/gtk/inspector/meson.build b/gtk/inspector/meson.build index 7d8fedec78..585855c422 100644 --- a/gtk/inspector/meson.build +++ b/gtk/inspector/meson.build @@ -35,6 +35,7 @@ inspector_sources = files( 'statistics.c', 'strv-editor.c', 'treewalk.c', + 'updatesoverlay.c', 'visual.c', 'window.c', ) diff --git a/gtk/inspector/updatesoverlay.c b/gtk/inspector/updatesoverlay.c new file mode 100644 index 0000000000..59cf9fe3ea --- /dev/null +++ b/gtk/inspector/updatesoverlay.c @@ -0,0 +1,281 @@ +/* + * Copyright © 2018 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#include "config.h" + +#include "updatesoverlay.h" + +#include "gtkintl.h" +#include "gtkwidget.h" + +#include "gsk/gskrendernodeprivate.h" + +/* duration before we start fading in us */ +#define GDK_DRAW_REGION_MIN_DURATION 50 * 1000 +/* duration when fade is finished in us */ +#define GDK_DRAW_REGION_MAX_DURATION 200 * 1000 + +typedef struct { + gint64 timestamp; + cairo_region_t *region; +} GtkUpdate; + +typedef struct { + GQueue *updates; + GskRenderNode *last; + GtkWidget *widget; + guint tick_callback; + gulong unmap_callback; +} GtkWidgetUpdates; + +struct _GtkUpdatesOverlay +{ + GtkInspectorOverlay parent_instance; + + GHashTable *toplevels; /* widget => GtkWidgetUpdates */ +}; + +struct _GtkUpdatesOverlayClass +{ + GtkInspectorOverlayClass parent_class; +}; + +G_DEFINE_TYPE (GtkUpdatesOverlay, gtk_updates_overlay, GTK_TYPE_INSPECTOR_OVERLAY) + +static void +gtk_update_free (gpointer data) +{ + GtkUpdate *region = data; + + cairo_region_destroy (region->region); + g_slice_free (GtkUpdate, region); +} + +static void +gtk_widget_updates_release_widget (GtkWidgetUpdates *updates) +{ + g_assert (updates->widget); + g_signal_handler_disconnect (updates->widget, updates->unmap_callback); + if (updates->tick_callback) + gtk_widget_remove_tick_callback (updates->widget, updates->tick_callback); + updates->tick_callback = 0; + updates->widget = NULL; +} + +static void +gtk_widget_updates_free (gpointer data) +{ + GtkWidgetUpdates *updates = data; + + g_queue_free_full (updates->updates, gtk_update_free); + g_clear_pointer (&updates->last, gsk_render_node_unref); + if (updates->widget) + gtk_widget_updates_release_widget (updates); + + g_slice_free (GtkWidgetUpdates, updates); +} + +static gboolean +gtk_widget_updates_tick (GtkWidget *widget, + GdkFrameClock *clock, + gpointer data) +{ + GtkWidgetUpdates *updates = data; + GtkUpdate *draw; + gint64 now; + + now = gdk_frame_clock_get_frame_time (clock); + + for (draw = g_queue_pop_tail (updates->updates); + draw != NULL && (now - draw->timestamp >= GDK_DRAW_REGION_MAX_DURATION); + draw = g_queue_pop_tail (updates->updates)) + { + gtk_update_free (draw); + } + + gdk_surface_queue_expose (gtk_widget_get_surface (widget)); + if (draw) + { + g_queue_push_tail (updates->updates, draw); + return G_SOURCE_CONTINUE; + } + else + { + updates->tick_callback = 0; + return G_SOURCE_REMOVE; + } +} + +static GtkWidgetUpdates * +gtk_update_overlay_lookup_for_widget (GtkUpdatesOverlay *self, + GtkWidget *widget, + gboolean create) +{ + GtkWidgetUpdates *updates = g_hash_table_lookup (self->toplevels, widget); + + if (updates || !create) + return updates; + + updates = g_slice_new0 (GtkWidgetUpdates); + updates->updates = g_queue_new (); + updates->widget = widget; + updates->unmap_callback = g_signal_connect_swapped (widget, "unmap", G_CALLBACK (gtk_widget_updates_release_widget), updates); + + g_hash_table_insert (self->toplevels, g_object_ref (widget), updates); + return updates; +} + +static void +gtk_widget_updates_add (GtkWidgetUpdates *updates, + gint64 timestamp, + cairo_region_t *region) +{ + GtkUpdate *update; + GList *l; + + update = g_slice_new0 (GtkUpdate); + update->timestamp = timestamp; + update->region = region; + for (l = g_queue_peek_head_link (updates->updates); l != NULL; l = l->next) + { + GtkUpdate *u = l->data; + cairo_region_subtract (u->region, region); + } + g_queue_push_head (updates->updates, update); + if (updates->tick_callback == 0) + updates->tick_callback = gtk_widget_add_tick_callback (updates->widget, gtk_widget_updates_tick, updates, NULL); +} + +static void +gtk_updates_overlay_snapshot (GtkInspectorOverlay *overlay, + GtkSnapshot *snapshot, + GskRenderNode *node, + GtkWidget *widget) +{ + GtkUpdatesOverlay *self = GTK_UPDATES_OVERLAY (overlay); + GtkWidgetUpdates *updates; + GtkUpdate *draw; + gint64 now; + GList *l; + + updates = gtk_update_overlay_lookup_for_widget (self, widget, TRUE); + now = gdk_frame_clock_get_frame_time (gtk_widget_get_frame_clock (widget)); + + if (updates->last) + { + cairo_region_t *diff; + + diff = cairo_region_create (); + gsk_render_node_diff (updates->last, node, diff); + if (cairo_region_is_empty (diff)) + cairo_region_destroy (diff); + else + gtk_widget_updates_add (updates, now, diff); + } + else + { + cairo_region_t *region; + graphene_rect_t bounds; + + gsk_render_node_get_bounds (node, &bounds); + region = cairo_region_create_rectangle (&(cairo_rectangle_int_t) { + floor (bounds.origin.x), + floor (bounds.origin.y), + ceil (bounds.origin.x + bounds.size.width) - floor (bounds.origin.x), + ceil (bounds.origin.y + bounds.size.height) - floor (bounds.origin.y) + }); + gtk_widget_updates_add (updates, now, region); + } + g_clear_pointer (&updates->last, gsk_render_node_unref); + updates->last = gsk_render_node_ref (node); + + for (l = g_queue_peek_head_link (updates->updates); l != NULL; l = l->next) + { + double progress; + guint i; + + draw = l->data; + + if (now - draw->timestamp < GDK_DRAW_REGION_MIN_DURATION) + progress = 0.0; + else if (now - draw->timestamp < GDK_DRAW_REGION_MAX_DURATION) + progress = (double) (now - draw->timestamp - GDK_DRAW_REGION_MIN_DURATION) + / (GDK_DRAW_REGION_MAX_DURATION - GDK_DRAW_REGION_MIN_DURATION); + else + break; + + for (i = 0; i < cairo_region_num_rectangles (draw->region); i++) + { + GdkRectangle rect; + + cairo_region_get_rectangle (draw->region, i, &rect); + gtk_snapshot_append_color (snapshot, + &(GdkRGBA) { 1, 0, 0, 0.4 * (1 - progress) }, + &GRAPHENE_RECT_INIT(rect.x, rect.y, rect.width, rect.height), + "Debug Updates<%g>", progress); + } + } +} + +static void +gtk_updates_overlay_queue_draw (GtkInspectorOverlay *overlay) +{ + GtkUpdatesOverlay *self = GTK_UPDATES_OVERLAY (overlay); + GHashTableIter iter; + gpointer widget; + + g_hash_table_iter_init (&iter, self->toplevels); + while (g_hash_table_iter_next (&iter, &widget, NULL)) + gdk_surface_queue_expose (gtk_widget_get_surface (widget)); +} + +static void +gtk_updates_overlay_dispose (GObject *object) +{ + GtkUpdatesOverlay *self = GTK_UPDATES_OVERLAY (object); + + g_hash_table_unref (self->toplevels); + + G_OBJECT_CLASS (gtk_updates_overlay_parent_class)->dispose (object); +} + +static void +gtk_updates_overlay_class_init (GtkUpdatesOverlayClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkInspectorOverlayClass *overlay_class = GTK_INSPECTOR_OVERLAY_CLASS (klass); + + overlay_class->snapshot = gtk_updates_overlay_snapshot; + overlay_class->queue_draw = gtk_updates_overlay_queue_draw; + + gobject_class->dispose = gtk_updates_overlay_dispose; +} + +static void +gtk_updates_overlay_init (GtkUpdatesOverlay *self) +{ + self->toplevels = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, gtk_widget_updates_free); +} + +GtkInspectorOverlay * +gtk_updates_overlay_new (void) +{ + return g_object_new (GTK_TYPE_UPDATES_OVERLAY, NULL); +} + diff --git a/gtk/inspector/updatesoverlay.h b/gtk/inspector/updatesoverlay.h new file mode 100644 index 0000000000..88c89d29da --- /dev/null +++ b/gtk/inspector/updatesoverlay.h @@ -0,0 +1,34 @@ +/* + * Copyright © 2018 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_UPDATES_OVERLAY_H__ +#define __GTK_UPDATES_OVERLAY_H__ + +#include "inspectoroverlay.h" + +G_BEGIN_DECLS + +#define GTK_TYPE_UPDATES_OVERLAY (gtk_updates_overlay_get_type ()) +G_DECLARE_FINAL_TYPE (GtkUpdatesOverlay, gtk_updates_overlay, GTK, UPDATES_OVERLAY, GtkInspectorOverlay) + +GtkInspectorOverlay * gtk_updates_overlay_new (void); + +G_END_DECLS + +#endif /* __GTK_UPDATES_OVERLAY_H__ */ diff --git a/gtk/inspector/visual.c b/gtk/inspector/visual.c index 6619fc7d17..4af9b9a8a2 100644 --- a/gtk/inspector/visual.c +++ b/gtk/inspector/visual.c @@ -20,6 +20,9 @@ #include "visual.h" +#include "updatesoverlay.h" +#include "window.h" + #include "gtkadjustment.h" #include "gtkbox.h" #include "gtkcomboboxtext.h" @@ -65,6 +68,7 @@ struct _GtkInspectorVisualPrivate GtkWidget *debug_box; GtkWidget *rendering_mode_combo; + GtkWidget *updates_switch; GtkWidget *baselines_switch; GtkWidget *layout_switch; GtkWidget *touchscreen_switch; @@ -75,6 +79,8 @@ struct _GtkInspectorVisualPrivate GtkWidget *texture_rectangle_switch; GtkAdjustment *focus_adjustment; + + GtkInspectorOverlay *updates_overlay; }; G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorVisual, gtk_inspector_visual, GTK_TYPE_SCROLLED_WINDOW) @@ -220,6 +226,41 @@ font_scale_entry_activated (GtkEntry *entry, update_font_scale (vis, factor, TRUE, FALSE); } +static void +updates_activate (GtkSwitch *sw, + GParamSpec *pspec, + GtkInspectorVisual *vis) +{ + GtkInspectorVisualPrivate *priv = vis->priv; + GtkInspectorWindow *iw; + gboolean updates; + + updates = gtk_switch_get_active (sw); + iw = GTK_INSPECTOR_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (vis))); + if (iw == NULL) + return; + + if (updates) + { + if (priv->updates_overlay == NULL) + { + priv->updates_overlay = gtk_updates_overlay_new (); + gtk_inspector_window_add_overlay (iw, priv->updates_overlay); + g_object_unref (priv->updates_overlay); + } + } + else + { + if (priv->updates_overlay != NULL) + { + gtk_inspector_window_remove_overlay (iw, priv->updates_overlay); + priv->updates_overlay = NULL; + } + } + + redraw_everything (); +} + static void baselines_activate (GtkSwitch *sw) { @@ -860,6 +901,7 @@ gtk_inspector_visual_class_init (GtkInspectorVisualClass *klass) gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/visual.ui"); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, rendering_mode_combo); + gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, updates_switch); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, direction_combo); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, baselines_switch); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, layout_switch); @@ -885,6 +927,7 @@ gtk_inspector_visual_class_init (GtkInspectorVisualClass *klass) gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, font_scale_entry); gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, font_scale_adjustment); + gtk_widget_class_bind_template_callback (widget_class, updates_activate); gtk_widget_class_bind_template_callback (widget_class, direction_changed); gtk_widget_class_bind_template_callback (widget_class, rendering_mode_changed); gtk_widget_class_bind_template_callback (widget_class, baselines_activate); diff --git a/gtk/inspector/visual.ui b/gtk/inspector/visual.ui index aaf475cef9..14433754b6 100644 --- a/gtk/inspector/visual.ui +++ b/gtk/inspector/visual.ui @@ -405,6 +405,33 @@ + + + 0 + + + 10 + 40 + + + Show Graphic Updates + start + baseline + 0.0 + + + + + end + baseline + 1 + + + + + + + 0 @@ -625,6 +652,7 @@ + diff --git a/gtk/inspector/window.c b/gtk/inspector/window.c index 13b0ff7b6c..adc55fcf1f 100644 --- a/gtk/inspector/window.c +++ b/gtk/inspector/window.c @@ -427,7 +427,7 @@ gtk_inspector_prepare_render (GtkWidget *widget, for (l = iw->overlays; l; l = l->next) { - gtk_inspector_overlay_snapshot (l->data, snapshot, widget); + gtk_inspector_overlay_snapshot (l->data, snapshot, node, widget); } gsk_render_node_unref (node); -- 2.30.2